package plg.grupo1.Lexico.StateMachineHierarchy;

import java.util.Vector;

import plg.grupo1.Lexico.TokenHierarchy.Token;
import plg.grupo1.Lexico.TokenHierarchy.TokenError;
import plg.grupo1.Lexico.reserveWords.EvaluadorRango;
import plg.grupo1.Lexico.reserveWords.NoExisteSimbolo;
import plg.grupo1.Lexico.reserveWords.SimboloNoTieneValorNumerico;
import plg.grupo1.Lexico.reserveWords.TablaSimbolos;
import plg.grupo1.Lexico.reserveWords.ValorFueraRango;


public class Contexto {

	//Tokens generados
	private static Vector<Token> listaTokens = new Vector<Token>();
	private static Vector<TokenError> listaErrores = new Vector<TokenError>();
	
	//buffer a procesar
	private static StringBuffer buffer = new StringBuffer();
	//puntero a una posicion del buffer
	private static int actualCharAt = 0;
	//lexamen procesado por el automta
	private static StringBuffer lexema = new StringBuffer();
	//numero de linea 
	private static Integer numLinea = 1;
	private static TablaSimbolos tabla = new TablaSimbolos();
	//estado actual de la máquina
	private static State currentState;
	//contexto del controlWord
	private static StringBuffer delimControl= new StringBuffer();
	private static StringBuffer numControl = new StringBuffer();
	
    private static Contexto INSTANCE = null;
	private Contexto() {}
 
    private synchronized static void createInstance() {
        if (INSTANCE == null) INSTANCE = new Contexto();
    }
    
    //constructor singleton
    public static Contexto getInstance() {
        if (INSTANCE == null) createInstance();
        return INSTANCE;
    }
    
    //borra el buffer de entrada
    public static void reset(){
    	//Tokens generados
    	listaTokens = new Vector<Token>();
    	listaErrores = new Vector<TokenError>();
    	
    	//buffer a procesar
    	buffer = new StringBuffer();
    	//puntero a una posicion del buffer
    	actualCharAt = 0;
    	//lexamen procesado por el automta
    	lexema = new StringBuffer();
    	//numero de linea 
    	numLinea = 1;
    	tabla = new TablaSimbolos();
    	//estado actual de la máquina
    	currentState=null;
    	//contexto del controlWord
    	delimControl= new StringBuffer();
    	numControl = new StringBuffer();
	}
    
    //borra el buffer de entrada
    public static void clearBuffer(){
    	buffer = new StringBuffer();
	}
   
    //buffer a procesar por la maquina de estados
    public static StringBuffer getBuffer(){
    	return buffer; 
	}
    
    //inserta en el buffer de entrada la cadena s
    public static void appendToBuffer(String s){
    	buffer.append(s);
    }
    
    public static void appendToControlWordNum(char c){
    	numControl.append(c);
    }
    
    public static void appendToControlWordDelim(char c){
    	delimControl.append(c);
    }
    
    public static StringBuffer getControlWordNum(){
    	return numControl;
    }
    
    public static StringBuffer getControlWordDelim(){
    	return delimControl;
    }
  
    //el control word que se encuentra en el lexema está acompañado 
    // de un valor numerico
	public static boolean isSetControlWordNum(){
		return numControl.length()>0;
	}

    //el control word que se encuentra en el lexema 
    //finaliza con un delimitador?
	public static boolean isSetControlWordDelim(){
		return delimControl.length()>0;
	}
	
    public static void clearControlWordContext(){
    	delimControl = new StringBuffer();
    	numControl   = new StringBuffer();
    }

    //comprueba si un determinado controlWord existe y si existe verifica si el valor numerico 
    //almacenado es correcto para ese controlWord
	public static void checkControlWord(StringBuffer controlWord) throws NoExisteSimbolo, SimboloNoTieneValorNumerico, ValorFueraRango{
		EvaluadorRango rango = null;
		rango = tabla.check(controlWord.toString());
		if(rango==null && Contexto.isSetControlWordNum())
			throw new SimboloNoTieneValorNumerico();
		if(rango!=null && Contexto.isSetControlWordNum())
			if(!rango.check(new Integer(Contexto.getControlWordNum().toString())))
				throw new ValorFueraRango();
	}
	
    //comprueba si un determinado controlWord existe y si existe verifica si el valor numerico 
    //almacenado es correcto para ese controlWord
	public static EvaluadorRango getControlWordNumericRange(StringBuffer controlWord) throws NoExisteSimbolo{
		return tabla.check(controlWord.toString());
	}
	
	public static Vector<Token> getTokens() {
		return listaTokens;
	}

	public static void addToken(Token token) {
		listaTokens.add(token);
	}

	public static Vector<TokenError> getError() {
		return listaErrores;
	}

	public static void addError(TokenError token) {
		listaErrores.add(token);
	}

	//devuelve el caracter al que apunta el cursor y deja posicionado  
	//el cursor en el siguiente caracter del buffer
	public static char getCharAndMoveToNext() {
		char c = buffer.charAt(actualCharAt);
		if (actualCharAt <= buffer.length())
			actualCharAt++;
		return c;
	}
	
	//devuelve al caracter actual al que apunta el cursor
	//no mueve el cursor
	public static char getActualChar() {
		return buffer.charAt(actualCharAt);
	}
	
	//
	
	public static void genErrorAndGoToInitialState(String found,String expected){
		//error
		Contexto.addError(new TokenError("ERROR en linea " + Contexto.getCurrentLineN()
							+ " expected "+ expected +" but Found "
							+ Contexto.getLexema() + " + '" + found + "'"));
		Contexto.clearLexema();
		Contexto.setCurrentState(State0.getInstance());
	}
	
	//avanza el cursor en una posición
	public static void moveToNextChar() {
		if (actualCharAt < buffer.length())
			actualCharAt++;
	}

	public static Integer actualCharAt(){
		return actualCharAt;
	} 
	public static void appendToLexema(char c) {
		lexema.append(c);
	}

	public static void increaseLine() {
		numLinea++;
	}

	//elimina el ultimo caracter añadido al lexema
	//no retrocede el cursor del buffer
	public static void removeLastCharFromLexema() {
		lexema.deleteCharAt(lexema.length() - 1);
	}

	public static void clearLexema() {
		lexema = new StringBuffer();
		//borramos el subContexto del controlWord 
		//en el caso de que lo hayamos usado
		clearControlWordContext();
	}

	//retrocede el cursor en una posición
	public static void moveToPreviousChar() {
		if (actualCharAt > 0) actualCharAt--;
	}

	public static StringBuffer getLexema() {
		return lexema;
	}

	//numero actual de linea
	public static Integer getCurrentLineN() {
		return numLinea;
	}

	//indica si se ha llegado al final de fichero
	public static Boolean isEof() {
		return buffer.length() == actualCharAt;
	}
	
	//indica si es un caracter de "escape" \n\r\0\t\\{}
	public static boolean isEscape(Character c) {
		return "\n\r\0\t\\{}".indexOf(c) != -1; //|| isEof();
	}
	
	public static TablaSimbolos getTabla() {
		return tabla;
	}

	public static void setCurrentState(State estado) {
		currentState = estado;
	}

	public static State getCurrentState() {
		return currentState;
	}
	
	//inicia el funcionamiento de la máquina de estados
	public static void goNext() {
		while(currentState!=null) currentState.goNext();
	}
}